/////////////////////////////////////////////////////////////////////////////
// Purpose:     Synchronization of resource access by processes and threads
// Author:      Jesus Gonzalez
// Modified by: 
// Copyright:   (c) 2003-2013 Jesus Gonzalez <jgonzalez@gdr-sistemas.com>
// License:     BSD License
/////////////////////////////////////////////////////////////////////////////

#include "SmartLib/Semaphore.h"
#include <semaphore.h>
#include <errno.h>
#include <string.h>

#include "Log.h"

class SemaphorePrvSys
{
public:
    SemaphorePrvSys() {};
    sem_t semaphore;
};

Semaphore::Semaphore(uint initialValue)
: m_prvsys(new SemaphorePrvSys)
{
    if( sem_init(&m_prvsys->semaphore, 0, initialValue) < 0 )
    {
        THROW_ERROR("Couldn't initialize unnamed semaphore (Error = %s)", strerror(errno));
    }
}

Semaphore::~Semaphore()
{
    delete m_prvsys;
}

void Semaphore::Hold()
{
    while(true)
    {
        if( sem_wait(&m_prvsys->semaphore) < 0 )
        {
            if( errno == EINTR ) continue;

            THROW_ERROR( "Failed to lock semaphore (Error=%s)", strerror(errno) );
        }
        break;
    }
}

bool Semaphore::TryHold()
{
    while(true)
    {
        if( sem_trywait(&m_prvsys->semaphore) < 0 )
        {
            if( errno == EINTR ) continue;
            if( errno == EAGAIN ) return false;

            THROW_ERROR( "Failed to try to lock semaphore (Error=%s)", strerror(errno) );
        }
        break;
    }
    return true;
}

void Semaphore::Release()
{
    if( sem_post(&m_prvsys->semaphore) < 0 )
    {
        THROW_ERROR( "Failed to release semaphore (Error=%s)", strerror(errno) );
    }
}

int Semaphore::getValue()
{
    int ret;
    if( sem_getvalue(&m_prvsys->semaphore, &ret) < 0 )
    {
        THROW_ERROR( "Failed to release semaphore (Error=%s)", strerror(errno) );
    }
    return ret;
}
